<?php
/**
 * WSCORE NOSQL
 * @author wsfuyibing <websearch@163.com>
 * @source [PATH]project/base/nosql.php
 * @date 2013-12-22
 */
defined('WSCORE') || exit('Access-Denied');
/**
* NoSQL[Redis]
*/
class nosql {
	/**
	 * 公共私有
	 */
	private static $_inits = 0;
	private static $_socket = null;
	private static $_timeout = 3;
	public static $pages = array();
	private static function _do($n, $p = array()){
		/**
		* 初始化Redis
		*/
		self::$_inits == 0 && self::_do_inits();
		/**
		* 重设
		*/
		is_array($p) || $p = array();
		array_unshift($p, $n);
		defined('YII_DEBUG') && Yii::trace("redis://".implode(', ', $p), '<div style="margin:2px;padding:1px;background-color:#f00"><div style="padding:5px;background-color:#ff0">nosql.redis</div></div>');
		/**
		* 执行
		*/
		$c = '*'.count($p)."\r\n"; foreach($p as $arg){
			$c.='$'.strlen($arg)."\r\n".$arg."\r\n";
		} fwrite(self::$_socket, $c);
		/**
		* 返回
		*/
		return self::_do_response();
	}
	private static function _do_inits(){
		$p = Yii::app()->getParams()->redis;
		/**
		 * 读取Redis配置
		 * @package setting/yiiconf.php
		 * @example array(
		 *     'server' => '127.0.0.1:6379', 
		 *     'pass' => '', 
		 *     'data' => 0
		 * )
		 */
		$p['server'] = wsc::isarr($p, 'server');
		$p['server'] || $p['server'] = '127.0.0.1:6379';
		/**
		 * 链接Redis
		 */
		self::$_socket = @stream_socket_client($p['server'], $errno, $error, self::$_timeout); if (self::$_socket){
			self::$_inits++;
			/**
			* 账号与库索引
			*/
			wsc::isarr($p, 'pass') && self::_do('AUTH', array($p['pass']));
			wsc::isarr($p, 'data') && self::_do('SELECT', array($p['data']));
			return true;
		} E('REDIS_ERROR', array('error' => $error));
	}
	private static function _do_response(){	
		/**
		* 无法读取当前链接下的数据
		*/
		if (($line = fgets(self::$_socket)) === false){
			E('REDIS_ERROR', array('error' => 'fget fail'));
		}
		/**
		* 结果分析
		* @var string $type, 返回类型
		* @var string $line, 返回内容
		*/
		$type = $line[0];
		$line = substr($line, 1, -2);
		/**
		* 状态
		*/
		if ($type == '+' || $type == ':'){
			return $line;
		}
		/**
		* 单积
		*/
		if ($type == '$'){
			if ($line == '-1'){
				return null;
			} $length = $line + 2; $data = ''; while ($length > 0){
				if (($block = fread(self::$_socket, $length)) === false){
					E('REDIS_ERROR', array('error' => 'fread fail'));
				} $data .= $block; $length -= (function_exists('mb_strlen') ? mb_strlen($block,'8bit') : strlen($block));
			} return substr($data, 0, -2);
		}
		/**
		* 多积
		*/
		if ($type == '*'){
			$count = (int) $line; $data = array(); for ($i = 0; $i < $count; $i++){
				$data[]=self::_do_response();
			} return $data;
		}
		/**
		* 错误
		*/
		if ($type == '-'){
			E('REDIS_ERROR', array('error' => $line));
		} E('REDIS_ERROR', array('error' => 'format'));
	}
	private static function _do_pages(& $p){
		/**
		* 初始化分页参数
		*/
		self::$pages = array(
			'count' => $p['count'], 'page' => $p['page'], 'pages' => $p['pages'], 
			'begine' => 1, 'finish' => $p['pages'], 
			'list' => '', 'prev' => '', 'next' => ''
		);
		/**
		* 分页区间
		*/
		$_begine = 1;
		$_finish = self::$pages['pages'];
		$_showpage = 10;
		if (self::$pages['pages'] > $_showpage){
			$_half = (int) $_showpage/2;
			$_begine = self::$pages['page'] - $_half; $_begine < 1 && $_begine = 1;
			$_finish = $_begine + $_showpage - 1; if ($_finish > self::$pages['pages']){
			$_finish = self::$pages['pages'];
			$_begine = $_finish - $_showpage + 1; }
		}
		
		self::$pages['begine'] = $_begine;
		self::$pages['finish'] = $_finish;

		/**
		* 分表列表
		*/
		if (self::$pages['begine'] > 1){
			self::$pages['prev'] = WDH($p['href'], array('page' => self::$pages['page']-1));
		} if (self::$pages['finish'] < self::$pages['pages']){
			self::$pages['next'] = WDH($p['href'], array('page' => self::$pages['page']+1));
		} for ($i = self::$pages['begine']; $i <= self::$pages['finish']; $i++){
			self::$pages['list'] .= ' <a href="'.WDH($p['href'], array('page' => $i)).'" class="n inline'.($i == self::$pages['page'] ? ' hover' : '').'">'.$i.'</a> ';
		}
	}
	/**
	* 原生方法
	* @example nosql::app('get', 'key');
	* @example nosql::app('hmset', 'key1', 'val1', 'key2', 'val2');
	*/
	public static function app(){
		$_ai = func_num_args(); if ($_ai){
			$_as = func_get_args(); $_fn = $_as[0]; unset($_as[0]); if (count($_as)){
				return self::_do($_fn, $_as);
			} return self::_do($_fn);
		} return null;
	}
	/**
	* 读取Redis-Hash数组
	*/
	public static function aset($k, $f){
		/**
		* 读取HASH, 返回数组
		* @param string $k, Redis-Key名称
		* @param array $f, 返回数组字段
		* @example nosql::aset('user:1', array('id', 'username'))
		*/
		$p = array_merge(array('hmget', $k), $f); 
		$v = C(array('nosql', 'app'), $p);
		return array_combine($f, $v);
	}
	/**
	 * 工具集
	 */
	public static function page(){
		/**
		* 配置分页
		* @return array, 数据返回
		*/
		$r = array('count' => 0, 'page' => 1, 'pages' => 1, 'perpage' => 10, 'offset' => 0, 'limit' => 0, 'href' => '');
		/**
		* 可变参数解析
		*/
		$_ai = func_num_args(); if ($_ai){
			$_as = func_get_args();
			/**
			* 数组
			* @example #M#->page(array('count' => 15, 'page' => 1, 'perpage' => 15))
			* @example #M#->page(array('count' => 15, 'page' => 1, 'perpage' => 15, 'href' => '#'))
			*/
			if ($_ai == 1 && is_array($_as[0])){
				if (wsc::isarr($_as, '0.count', '>1')){
					$r['count'] = (int) $_as[0]['count'];
				} if (wsc::isarr($_as, '0.page', '>1')){
					$r['page'] = (int) $_as[0]['page'];
				} if (wsc::isarr($_as, '0.perpage', '>1')){
					$r['perpage'] = (int) $_as[0]['perpage'];
				} if (wsc::isarr($_as, '0.href')){
					$r['href'] = $_as[0]['href'];
				}
			}
			/**
			* 混合参数
			* @example #M#->page(150, 1)
			* @example #M#->page(150, 1, 15)
			* @example #M#->page(150, 1, 15, '#')
			*/
			else if ($_ai) {
				is_numeric($_as[0]) && $_as[0] >= 1 && $r['count'] = (int) $_as[0];
				$_ai > 1 && is_numeric($_as[1]) && $_as[1] >= 1 && $r['page'] = (int) $_as[1];
				$_ai > 2 && is_numeric($_as[2]) && $_as[2] >= 1 && $r['perpage'] = (int) $_as[2];
				$_ai > 3 && $_as[3] && $r['href'] = $_as[3];
			}
		}
		/**
		* 范围计算
		*/
		if ($r['count']){
			$r['pages'] = ceil($r['count'] / $r['perpage']);
			$r['page'] > $r['pages'] && $r['page'] = $r['pages'];
			/**
			* 分页链接
			*/
			//if ($r['href']){
				self::_do_pages($r);
			//}
		}
		/**
		* 总页码
		*/
		$r['offset'] = ($r['page'] - 1) * $r['perpage'];
		$r['offset'] < 0 && $r['offset'] = 0;
		$r['limit']  = $r['perpage'];
		/**
		* 极限
		*/
		if ($r['page'] == $r['pages']){
			$r['limit'] = $r['count'] - ($r['page']-1) * $r['perpage'];
		} return $r;
	}
	public static function popas($c, $a, $i){
		/**
		* 可变参数
		* @param int $c, 总量
		* @param array $a, 参数
		* @param int $i, 剔除量
		*/
		$c = is_numeric($c) ? (int) $c : 0;
		$c < 0 && $c = 0;
		/**
		* 可变参数剔除
		*/
		is_array($a) || $a = array(); $_ai = count($a); $i = is_numeric($i) ? (int) $i : 0; if ($_ai){
			$i = min($i, $_ai); for ($n = 0; $n < $i; $n++){
				array_shift($a);
			}
		} array_unshift($a, $c);
		/**
		* 分页返回
		*/
		return C(array('nosql', 'page'), $a);
	}
    public static function limits($a, $p, $i = 0){
		/**
		* 提取数组$a的前n条记录
        * @param int $p, 总量
        * @paran int $i, 偏移量
		*/
		$r = array(); if (is_array($a)){
			$l = count($a); if ($l){
				for ($n = $i; $n < $p; $n++){
					$r[] = $a[$n];
				}
			}
		} return $r;
	}
	
}